// package: rewords-server-2024
// filename: admin
// author: diogoxiang@gmail.com
// time: 2024/11/8 14:05

package admin

import (
	"context"
	"rewords-server-2024/internal/model/input/form"
	"rewords-server-2024/utility/ufun"
	"rewords-server-2024/utility/ztime"
	"sort"

	"go.mongodb.org/mongo-driver/bson"

	"github.com/gogf/gf/v2/errors/gerror"
	"github.com/gogf/gf/v2/frame/g"
	"github.com/gogf/gf/v2/util/gconv"

	"rewords-server-2024/api/admin"
	"rewords-server-2024/internal/consts"
	"rewords-server-2024/internal/library/ecode"
	"rewords-server-2024/internal/logic/mgx"
	"rewords-server-2024/internal/model/entity"
	"rewords-server-2024/internal/model/input/admin_in"
	"rewords-server-2024/internal/model/input/query_in"
	"rewords-server-2024/internal/service"
)

var (
	Menu = new(cMenu)
)

type cMenu struct{}

// List 菜单列表 适用于 后台编辑
func (c *cMenu) List(ctx context.Context, req *admin.MenuListReq) (res *admin.MenuListRes, err error) {

	// 添加 etype="dict" 条件
	etypeCondition := query_in.Condition{
		Field:         "etype",
		ConditionType: query_in.ConditionTypeEq,
		Vals:          []any{"menu"},
		Op:            query_in.OpAnd,
	}

	if req.Conditions == nil {
		req.Conditions = []query_in.Condition{etypeCondition}
	} else {
		req.Conditions = append(req.Conditions, etypeCondition)
	}

	query, _, _ := query_in.BuildMongoQuery(req.Conditions, nil, []string{})
	g.Log().Infof(ctx, "MongoDB query conditions:", query)

	// 数量特殊处理 .1000
	result, err := service.Mgx().Find(ctx, consts.CConfig, query, nil, req.Offset, consts.DefaultLimit, nil)

	if err != nil {
		g.Log().Error(ctx, "Failed to fetch menu list:", err)
		return nil, gerror.NewCode(ecode.DataError)
	}

	//g.Dump(result)

	// 构建树状结构
	menuTree := make([]*admin_in.MenuTree, 0)
	menuMap := make(map[int64]*admin_in.MenuTree)

	// 第一次遍历：创建所有节点的映射
	for _, item := range result {
		menu := &admin_in.MenuTree{
			SysMenu: entity.SysMenu{
				ID:        item["id"].(int64),
				ParentID:  item["parent_id"].(int64),
				Name:      item["name"].(string),
				Icon:      item["icon"].(string),
				Path:      item["path"].(string),
				Redirect:  item["redirect"].(string),
				SchemaAPI: item["schema_api"].(string),
				Status:    item["status"].(string),
				Visible:   item["visible"].(int32),
				IsSys:     item["is_sys"].(int32),
				SortNo:    item["sort_no"].(int32),
				Remark:    item["remark"].(string),
				PublicTime: entity.PublicTime{
					CreatedAt: ztime.ConvertMongoTime(item["created_at"]),
					UpdatedAt: ztime.ConvertMongoTime(item["updated_at"]),
				},
			},
			Key:      item["id"].(int64),    // 使用 id 作为 key
			Label:    item["name"].(string), // 使用 name 作为 label
			Children: make([]*admin_in.MenuTree, 0),
		}
		menuMap[menu.ID] = menu
	}

	// 第二次遍历：构建树状结构
	for _, menu := range menuMap {
		_, exists := menuMap[menu.ParentID]
		if menu.ParentID == 0 || !exists {
			// 如果是根节点或父节点不存在，则作为顶层节点
			menuTree = append(menuTree, menu)
		} else {
			if parent, exists := menuMap[menu.ParentID]; exists {
				parent.Children = append(parent.Children, menu)
				// 每次添加子节点后立即进行稳定排序
				if len(parent.Children) > 1 {
					sort.SliceStable(parent.Children, func(i, j int) bool {
						if parent.Children[i].SortNo == parent.Children[j].SortNo {
							return parent.Children[i].Name < parent.Children[j].Name
						}
						return parent.Children[i].SortNo < parent.Children[j].SortNo
					})
				}
			}
		}
	}

	// 对根节点进行稳定排序
	sort.SliceStable(menuTree, func(i, j int) bool {
		if menuTree[i].SortNo == menuTree[j].SortNo {
			return menuTree[i].Name < menuTree[j].Name
		}
		return menuTree[i].SortNo < menuTree[j].SortNo
	})

	// 返回树状结构
	return &admin.MenuListRes{
		MenuTreeModel: admin_in.MenuTreeModel{
			List: menuTree,
		},
	}, nil
}

// Fetch 获取菜单树
func (c *cMenu) Fetch(ctx context.Context, req *admin.MenuSimpleReq) (res *admin.MenuSimpleRes, err error) {

	selectMap := bson.M{
		"etype": "menu",
	}

	menuList, err := service.Mgx().Find(ctx, consts.CConfig, selectMap, nil, 0, 1000, nil)
	if err != nil {
		g.Log().Error(ctx, "Failed to fetch menu list:", err)
		return nil, gerror.NewCode(ecode.DataError)
	}

	if len(menuList) == 0 {
		return &admin.MenuSimpleRes{
			Pages: make([]*admin_in.MenuSimple, 0),
		}, nil
	}
	// 构建树状结构
	menuTree := make([]*admin_in.MenuSimple, 0)
	menuMap := make(map[int64]*admin_in.MenuSimple)

	// 第一次遍历：创建所有节点的映射
	for _, item := range menuList {
		menu := &admin_in.MenuSimple{
			ParentID:  item["parent_id"].(int64),
			Label:     item["name"].(string),
			Url:       item["path"].(string),
			Icon:      item["icon"].(string),
			Redirect:  item["redirect"].(string),
			Visible:   item["visible"].(int32) == 1,
			SortNo:    item["sort_no"].(int32),
			SchemaAPI: item["schema_api"].(string),
			Children:  make([]*admin_in.MenuSimple, 0),
		}
		menuMap[item["id"].(int64)] = menu
	}

	// 第二次遍历：构建树状结构
	for _, menu := range menuMap {
		_, exists := menuMap[menu.ParentID]
		if menu.ParentID == 0 || !exists {
			menuTree = append(menuTree, menu)
		} else {
			if parent, exists := menuMap[menu.ParentID]; exists {
				parent.Children = append(parent.Children, menu)
				// 每次添加子节点后立即对子节点进行稳定排序
				if len(parent.Children) > 1 {
					sort.SliceStable(parent.Children, func(i, j int) bool {
						if parent.Children[i].SortNo == parent.Children[j].SortNo {
							// 当排序号相同时，可以使用其他字段作为次要排序条件
							return parent.Children[i].Label < parent.Children[j].Label
						}
						return parent.Children[i].SortNo < parent.Children[j].SortNo
					})
				}
			}
		}
	}

	// 对根节点进行稳定排序
	sort.SliceStable(menuTree, func(i, j int) bool {
		if menuTree[i].SortNo == menuTree[j].SortNo {
			return menuTree[i].Label < menuTree[j].Label
		}
		return menuTree[i].SortNo < menuTree[j].SortNo
	})

	return &admin.MenuSimpleRes{
		Pages: menuTree,
	}, nil
}

func (c *cMenu) Options(ctx context.Context, req *admin.MenuKvReq) (res *admin.MenuKvRes, err error) {
	selectMap := bson.M{
		"etype":   "menu",
		"visible": ufun.If(req.Visible, 1, 0),
	}

	result, err := service.Mgx().Find(ctx, consts.CConfig, selectMap, nil, 0, 1000, nil)
	if err != nil {
		g.Log().Error(ctx, "Failed to fetch menu list:", err)
		return nil, gerror.NewCode(ecode.DataError)
	}

	// 创建根目录节点
	rootMenu := &admin_in.MenuKv{
		ParentID: 0,
		Label:    "根目录",
		Value:    0,
		SortNo:   0,
		Children: make([]*admin_in.MenuKv, 0),
	}

	// 创建ID到节点的映射
	menuMap := make(map[int64]*admin_in.MenuKv)
	menuMap[rootMenu.Value] = rootMenu

	// 第一次遍历：创建所有节点
	for _, item := range result {
		id := item["id"].(int64)
		menu := &admin_in.MenuKv{
			ParentID: item["parent_id"].(int64),
			Label:    item["name"].(string),
			Value:    id,
			SortNo:   item["sort_no"].(int32),
			Children: make([]*admin_in.MenuKv, 0),
		}
		menuMap[id] = menu
	}

	// 第二次遍历：构建树状结构
	for _, menu := range menuMap {
		if menu.Value == 0 {
			continue // 跳过根节点
		}
		if parent, exists := menuMap[menu.ParentID]; exists {
			parent.Children = append(parent.Children, menu)
		}
	}

	// 递归排序所有层级的节点
	sortMenuTree(rootMenu.Children)

	// 创建包含根节点的选项数组
	options := []*admin_in.MenuKv{rootMenu}

	return &admin.MenuKvRes{
		Options: options,
	}, nil
}

// sortMenuTree 递归排序菜单树
func sortMenuTree(nodes []*admin_in.MenuKv) {
	if len(nodes) <= 1 {
		return
	}

	// 对当前层级进行排序
	sort.SliceStable(nodes, func(i, j int) bool {
		if nodes[i].SortNo == nodes[j].SortNo {
			return nodes[i].Label < nodes[j].Label
		}
		return nodes[i].SortNo < nodes[j].SortNo
	})

	// 递归排序子节点
	for _, node := range nodes {
		if len(node.Children) > 0 {
			sortMenuTree(node.Children)
		}
	}
}

// AddMenu 添加菜单
func (c *cMenu) AddMenu(ctx context.Context, req *admin.MenuAddReq) (res *admin.MenuAddRes, err error) {

	menuDocs := &entity.SysMenu{
		ParentID:  req.ParentID,
		Name:      req.Name,
		Icon:      req.Icon,
		Path:      req.Path,
		Redirect:  req.Redirect,
		SchemaAPI: req.SchemaAPI,
		Status:    req.Status,
		Visible:   req.Visible,
		IsSys:     req.IsSys,
		SortNo:    req.SortNo,
		Remark:    req.Remark,
		Etype:     "menu",
	}
	uid, err := mgx.GetAutoInc("menu")
	if err != nil {
		// 处理错误，例如记录日志或返回错误信息
		g.Log().Printf(ctx, "Failed to get auto increment value for user: %v", err)
		// 根据实际情况决定如何处理错误，例如返回一个错误值或中断程序
		return nil, gerror.NewCode(ecode.InvalidParams)
	}
	menuDocs.ID = int64(uid)
	menuDocs.SetTimeValue(0)
	menuDocs.SetTimeValue(1)

	err = service.Mgx().InsertOne(ctx, consts.CConfig, menuDocs)

	if err != nil {
		g.Log().Error(ctx, "Insert error:", err)
		return nil, gerror.NewCode(ecode.InvalidParams)
	}

	// 插入数据
	return &admin.MenuAddRes{
		Id: int64(uid),
	}, nil
}

// UpdateMenu 更新菜单
func (c cMenu) UpdateMenu(ctx context.Context, req *admin.MenuUpdateReq) (res *admin.MenuUpdateRes, err error) {

	updataDoc := form.NewBuilder().
		Set("parent_id", req.ParentID). // 可是能为0
		SetIfNotEmpty("name", req.Name).
		Set("icon", req.Icon).
		Set("path", req.Path).
		Set("redirect", req.Redirect).
		Set("schema_api", req.SchemaAPI).
		SetIfNotZero("status", req.Status).
		SetIfNotZero("visible", req.Visible).
		SetIfNotZero("sort_no", req.SortNo).
		SetIfNotEmpty("remark", req.Remark).
		Set("updated_at", ztime.GetLocalTime()).
		Build()

	// 执行更新操作
	result, err := service.Mgx().UpdateOne(ctx,
		consts.CConfig,
		bson.M{"id": req.ID, "etype": "menu"},
		bson.M{"$set": updataDoc},
	)
	if err != nil {
		g.Log().Error(ctx, "Update error:", err)
		return nil, gerror.NewCode(ecode.InvalidParams)
	}

	if result.MatchedCount == 0 {
		return nil, gerror.NewCode(ecode.DataNotFound)
	}

	return &admin.MenuUpdateRes{}, nil
}

// DeleteMenu 删除菜单 标记删除
func (c *cMenu) DeleteMenu(ctx context.Context, req *admin.MenuDeleteReq) (res *admin.MenuDeleteRes, err error) {
	updateDoc := form.NewBuilder().
		Set("deleted_at", ztime.GetLocalTime()).
		Build()

	result, err := service.Mgx().UpdateOne(ctx,
		consts.CConfig,
		bson.M{"id": req.Ids, "etype": "menu"},
		bson.M{"$set": updateDoc},
	)

	if err != nil {
		g.Log().Error(ctx, "Update error:", err)
		return nil, gerror.NewCode(ecode.InvalidParams)
	}

	if result.MatchedCount == 0 {
		return nil, gerror.NewCode(ecode.DataNotFound)
	}

	return &admin.MenuDeleteRes{}, err
}

// BatchUpdateMenu 批量更新菜单 主要用于更新状态
func (c *cMenu) BatchUpdateMenu(ctx context.Context, req *admin.MenuBatchUpdateReq) (res *admin.MenuBatchUpdateRes, err error) {
	// rows 是 一个数组 类似 [{id:12,status:normal},..]
	updatedIds := make([]int64, 0)

	for _, item := range req.Rows {
		// 使用类型断言来获取 map 中的值
		id := gconv.Int64(item["id"])
		// if !ok {
		// 	g.Log().Errorf(ctx, "Invalid ID format in row: %v", item)
		// 	continue
		// }

		status, ok := item["status"].(string)
		if !ok {
			g.Log().Errorf(ctx, "Invalid status format in row: %v", item)
			continue
		}

		updateDoc := form.NewBuilder().
			SetIfNotEmpty("status", status).
			Set("updated_at", ztime.GetLocalTime()).
			Build()

		result, err := service.Mgx().UpdateOne(ctx,
			consts.CConfig,
			bson.M{"id": id, "etype": "menu"},
			bson.M{"$set": updateDoc},
		)

		if err != nil {
			g.Log().Errorf(ctx, "Failed to update menu %d: %v", id, err)
			continue
		}

		if result.MatchedCount > 0 {
			updatedIds = append(updatedIds, id)
		}
	}

	return &admin.MenuBatchUpdateRes{
		Rows: updatedIds,
	}, nil
}
